/**
 * <p>文件名:		SocketUtils.java</p>
 * <p>版权:		CopyrightTag</p>
 * <p>公司:		CompanyTag</p>
 * @author		周华彬(zhouhuabin@ctfo.com)
 */

package com.caits.lbs.framework.utils;

import java.io.BufferedReader;
import java.io.Console;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

import org.apache.logging.log4j.Logger;

import com.caits.lbs.framework.log.CommonLogFactory;

/**
 * <p>
 * SocketUtils
 * </p>
 * <p>
 * 网络相关的工具类,
 * main可以用来测试两台机器之间tcp连接的可用性
 * </p>
 * 
 * @author 周华彬(zhouhuabin@ctfo.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th * width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>周华彬</td>
 *          <td>2008-10-18 下午01:31:24</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public abstract class SocketUtils {

	/** 日志记录器 */
	protected static Logger log = CommonLogFactory.getLog();

	/**
	 * FIXME
	 * 
	 * @param socket
	 * @param in
	 * @param out
	 */
	public static void close(Socket socket, InputStream in, OutputStream out) {
		try {
			if (in != null) {
				in.close();
			}
		} catch (Exception e) {

		} finally {
			in = null;
		}
		try {
			if (out != null) {
				out.close();
			}
		} catch (Exception e) {

		} finally {
			out = null;
		}
		try {
			if (socket != null) {
				socket.close();
			}
		} catch (Exception e) {

		} finally {
			socket = null;
		}
	}

	/**
	 * 关闭网络连接相关的流
	 * 
	 * @param socket
	 * @param in
	 * @param out
	 */
	public static void close(Socket socket, BufferedReader in, PrintWriter out) {
		try {
			if (in != null) {
				in.close();
			}
		} catch (Exception e) {

		} finally {
			in = null;
		}
		try {
			if (out != null) {
				out.close();
			}
		} catch (Exception e) {

		} finally {
			out = null;
		}
		try {
			if (socket != null) {
				socket.close();
			}
		} catch (Exception e) {

		} finally {
			socket = null;
		}
	}

	/**
	 * FIXME
	 * 
	 * @param socket
	 * @param in
	 * @param out
	 */
	public static void close(Socket socket, BufferedReader in, DataOutputStream out) {
		try {
			if (in != null) {
				in.close();
			}
		} catch (Exception e) {

		} finally {
			in = null;
		}
		try {
			if (out != null) {
				out.close();
			}
		} catch (Exception e) {

		} finally {
			out = null;
		}
		try {
			if (socket != null) {
				socket.close();
			}
		} catch (Exception e) {

		} finally {
			socket = null;
		}
	}

	public static void close(Socket socket, BufferedReader in, PrintStream out) {
		try {
			if (in != null) {
				in.close();
			}
		} catch (Exception e) {

		} finally {
			in = null;
		}
		try {
			if (out != null) {
				out.close();
			}
		} catch (Exception e) {

		} finally {
			out = null;
		}
		try {
			if (socket != null) {
				socket.close();
			}
		} catch (Exception e) {

		} finally {
			socket = null;
		}
	}

	/**
	 * 检验socket是否有效
	 * 
	 * @param socket
	 * @return
	 */
	public static boolean validSocket(Socket socket) {
		return socket != null && socket.isBound() && !socket.isClosed() && socket.isConnected() && !socket.isInputShutdown() && !socket.isOutputShutdown();
	}

	/**
	 * 根据地址和端口创建到远程的socket连接
	 * 
	 * @param address
	 * @param port
	 * @return
	 */
	public static Socket createRemoteSocket(String address, int port, int timeOut, Logger log) throws SocketException {
		if (port < 1) {
			throw new IllegalArgumentException("目标地址参数不正确" + address);
		}
		Socket socket = null;
		try {
			socket = new Socket();
			socket.setReuseAddress(true);
			socket.setKeepAlive(true); // Will monitor the TCP connection is
										// valid
			socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
										// ensure timely delivery of data
			socket.setSoLinger(true, 0); // Control calls close () method, the
											// underlying socket is closed
											// immediately

			if (timeOut > 0)
				socket.setSoTimeout(timeOut);
			socket.connect(new InetSocketAddress(address, port));
			socket.setKeepAlive(true);
			log.info("网络连接成功," + address + ":" + port);

		} catch (SocketException e) {
			log.error("创建socket异常," + e.getLocalizedMessage());
			throw e;
		} catch (IOException e) {
			log.error("连接远程服务器IO出错," + e.getLocalizedMessage(), e);
			throw new SocketException("网络IO错误," + e.getLocalizedMessage());
		}
		return socket;
	}

	// java -client servername port
	// java -server port
	public static void main(String[] args) throws Throwable {
		if (args.length < 1) {
			printUsage();
			System.exit(-1);
		}
		if ("-client".equals(args[0])) {
			SocketIO sio = new SocketIO(new Socket(args[1], Integer.parseInt(args[2])));
			try {
				System.out.printf("access connected to %s", sio.s.getRemoteSocketAddress().toString());
				String ln;
				Console con = System.console();
				while ((ln = sio.read()) != null) {
					con.format("ECHO: %s", ln);
					ln = con.readLine("%nMe  : ");
					if (ln != null)
						sio.write(ln);
					if ("exit".equals(ln))
						break;
				}
			} catch (IOException ioe) {
				ioe.printStackTrace();
			} finally {
				sio.close();
			}
		} else if ("-server".equals(args[0])) {
			ServerSocket ss = new ServerSocket(Integer.parseInt(args[1]));
			System.out.printf("start listen on %d", ss.getLocalPort());
			while (true) {
				new SSThread(ss.accept()).start();
			}
		} else {
			printUsage();
		}
	}

	/**
	 * 打印用法
	 */
	private static void printUsage() {
		System.out.printf("usage:%s -client servername port\r\n", SocketUtils.class.getCanonicalName());
		System.out.printf("usage:%s -server port\r\n", SocketUtils.class.getCanonicalName());
	}

	/**
	 * Wraps a connected Socket object. It then can be used to read bytes
	 * from/write bytes into the underlying socket streams.
	 */
	private static class SocketIO {
		private Socket s;
		private OutputStream os;
		private InputStream is;

		SocketIO(Socket s) throws IOException {
			this.s = s;
//			this.os = s.getOutputStream();
//			this.is = s.getInputStream();
			this.os = new DeflaterOutputStream(s.getOutputStream());
			this.is = new InflaterInputStream(s.getInputStream());
		}

		byte[] bb = new byte[1024];

		String read() throws IOException {
			int n = is.read(bb);
			return new String(bb, 0, n);
		}

		void write(String ln) throws IOException {
			os.write(ln.getBytes());
			os.flush();
		}

		void close() {
			try {
				s.close();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
	}

	/**
	 * A worker Thread that wraps a Socket object returned from
	 * ServerSocket.accept() and echos every byte read from client.
	 */
	static class SSThread extends Thread {
		private SocketIO sio;

		public SSThread(Socket s) throws IOException {
			this.sio = new SocketIO(s);
		}

		public void run() {
			try {
				sio.write("Welcome to ECHO!");
				String ln;
				while ((ln = sio.read()) != null) {
					sio.write(ln);
					if ("exit".equals(ln))
						break;
				}
			} catch (IOException ioe) {
				ioe.printStackTrace();
			} finally {
				sio.close();
			}
		}
	}
}
